iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 25
1
Modern Web

RRR撞到不負責之 Laravel + Nuxt.js 踩坑全紀錄系列 第 25 篇

Day 25. 說好的 window 和 document 呢?

  • 分享至 

  • xImage
  •  

在傳統許多 JS 套件都是和畫面渲染以及 HTML Dom 有關。然而 Nuxt 是 SSR,因此在使用套件 (或是自己開發) 的過程中,可能會遇到「window 和 document 沒有定義」這樣的錯誤訊息,接下來會介紹如何排除錯誤!

no-ssr & process.client

在 Nuxt 預設 component 裡面有一個 <no-ssr>,此 component 將在 Nuxt 3.X 中捨棄,所以我們依照官方提示改用 <client-only>。

<client-only> 顧名思義,在這個 component 底下的內容都會在 client 端渲染。通常會使用 Nuxt 多半都與 SSR 和 SEO 兩大需求脫離不了關係,所以如果要讓部分畫面由 client 端渲染,要稍微注意是否跟原始需求有衝突!

下面是一段很單純的 code,我們透過 process.client 與 process.server 來確定 <client-only> 底下內容的渲染時機。

<template>
    <div>
        <div>
            A 區塊是由 {{renderSide()}} 渲染
        </div>
        <client-only>
            B 區塊是由 {{renderSide()}} 渲染
        </client-only>
    </div>
</template>

<script>
export default {
    name: 'index',
    methods: {
        renderSide() {
            if (process.server) {
                return 'Server 端';
            } else if (process.client) {
                return 'Client 端';
            }
        },
    }
}
</script>

什麼!! 你說 A、B 區塊都是顯示 Client 端 嗎? 別緊張我們看看 console:

https://ithelp.ithome.com.tw/upload/images/20190926/20112580gFeoXwm0Gn.png

看吧! A 區塊確實是在 server 端渲染好的,同時 B 區塊也只在 client 端的時候才渲染。

window is not defined

就像開頭說的,有些套件可能會用到 window 或是 document 這類前端及 browser 的物件,因此在引用、使用套見的時候就會出錯。

  1. 假設我們有個 JS module 匯出 Post 類別
// 我知道 alert 訊息很詭異,但只是個範例 ><"
window.alert('Using Post ...');

export class ITPost {
    constructor(title, content) {
        this.title = title;
        this.content = content;
    }

    setTitle(title) {
        this.title = title;
    }

    setContent(content) {
        this.content = content;
    }

    getTitle() {
        return this.title;
    }

    getContent() {
        return this.content;
    }
}
  1. 我們建立一個 post component 並利用 ITPost 類別
<template>
    <div>
        <div>{{itPost.getTitle()}}</div>
        <div>{{itPost.getContent()}}</div>
    </div>
</template>

<script>
import { ITPost } from '../helpers/ITPost';

export default {
    name: 'Post',
    props: {
        post: {
            required: true,
            type: Object,
        }
    },
    data() {
        return {
            itPost: undefined,
        };
    },
    mounted() {
        this.itPost = new ITPost(post.title, post.content);
    }
}
</script>
  1. 最後我們在 page component 上使用。
<template>
    <div>
        <it-post :post="post"></it-post>
    </div>
</template>

<script>
import ItPost from '../components/ItPost';

export default {
    name: 'test',
    components: {ItPost},
    data() {
        return {
            post: {
                title: 'Day 23. Vuex 和 Cookie 哪個好? 小朋友才做決定,我兩個都要',
                content: '...'
            }
        };
    }
}
</script>
  1. 一切看似沒問題,實際跑起來卻出現「window is not defined」
    https://ithelp.ithome.com.tw/upload/images/20190926/201125804RZqTIeraa.png

  2. 好的,那我們在 page component 加上 <client-only>

<template>
    <div>
        <client-only>
            <it-post :post="post"></it-post>
        </client-only>
    </div>
</template>

結果還是錯RRR!

  1. 回到 ItPost.vue,我們將 import 改為 process.client + require() 就好了!
<template>
    <div>
        <!-- 確保 itPost 是存在的 -->
        <template v-if="itPost">
            <div>{{itPost.getTitle()}}</div>
            <div>{{itPost.getContent()}}</div>
        </template>
    </div>
</template>

<script>
export default {
    name: 'Post',
    props: {
        post: {
            required: true,
            type: Object,
        }
    },
    data() {
        return {
            itPost: undefined,
        };
    },
    mounted() {
        // 只有在 client 端才執行
        if (process.client) {
            const { ITPost } = require('../helpers/ITPost');
            this.itPost = new ITPost(this.post.title, this.post.content);
        }
    }
}
</script>

我們從今天的範例可以看到,如果套件或是一些 Vue component 有用到 (更精確的說法是有執行到) 一些屬於前端和瀏覽器才有的相關物件與變數,我們可以透過 <client-only> 搭配 process.client 調整某些套件或是 component 要使用的時機!

Nuxt 的介紹到這邊告一個段落,明天開始要來寫寫幾個常用到的範例 (總不能連 Hello World 都沒有吧) ! 但是在結束之前,我們回顧 Nuxt lifecycle

https://ithelp.ithome.com.tw/upload/images/20190926/20112580A08rj9zkVE.png

Nuxt lifecycle 圖源自於官網

不知道在最後看到這張圖有沒有感覺呢?

首先透過 nuxtServerInit 將 request 中的 cookie 重新塞回 Vuex state;接著先透過 route 對應上的所有 component 中的 middleware 過濾與驗證,接著可以透過 validate() 確認 route 上的變數是否合法,通過驗證之後,藉由 asyncData() 與 fetch() 串接 API 取得資料,必要時可以存入 Vuex state 當中,最後我們渲染畫面。這就是一個基礎的 page component 完整 lifecycle。

個人覺得從 Vue 轉到 Nuxt 沒有太多困擾 (當然也可能因為沒有寫到 scale 很大的案子),但是就學習曲線而言應該算是平易近人的,不知道大家覺得如何呢?

那今天就先這樣,我們明天見!


上一篇
Day 24. Plugin - 懶人救星
下一篇
Day 26. 手把手造個輪子 - Form 表單 (文長慎入)
系列文
RRR撞到不負責之 Laravel + Nuxt.js 踩坑全紀錄 共 31 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言